﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using SharePoint.Orm.Caml;
using SharePoint.Orm.Helpers;

namespace SharePoint.Orm {
    public class SPQueryTranslator : ExpressionVisitor {
        private StringBuilder _camlBuilder;
        public string CamlString { get { return _camlBuilder.ToString(); } }
        public string Translate(Expression expression) {
            _camlBuilder = new StringBuilder();
            LambdaExpression lambda = (LambdaExpression) expression;
            Visit(lambda.Body);
            return _camlBuilder.ToString();
        }
        protected override Expression VisitMethodCall(MethodCallExpression m) {
            if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where") {
                _camlBuilder.Append("<Where>");
                Visit(m.Arguments[1]);
                _camlBuilder.Append("</Where>");
                return m;
            }
            return base.VisitMethodCall(m);
        }
        protected override Expression VisitUnary(UnaryExpression u) {
            return Visit(u.Operand);
        }
        protected override Expression VisitConstant(ConstantExpression c) {
            _camlBuilder.Append(string.Format("<Value Type='{0}'>{1}</Value>", c.Type, c.Value));
            return c;
        }
        protected override Expression VisitParameter(ParameterExpression p) {
            _camlBuilder.Append(string.Format("<FieldRef Name='{0}'/>", p.Name));
            return p;
        }
        protected override Expression VisitMemberAccess(MemberExpression m) {
            _camlBuilder.Append(string.Format("<FieldRef Name='{0}'/>",m.Member.Name));
            return m;
        }
        protected override Expression VisitBinary(BinaryExpression b) {
            switch (b.NodeType) {
                case ExpressionType.AndAlso:
                    _camlBuilder.Append("<And>");
                    VisitBoth(b);
                    _camlBuilder.Append("</And>");
                    break;
                case ExpressionType.OrElse:
                    _camlBuilder.Append("<Or>");
                    VisitBoth(b);
                    _camlBuilder.Append("</Or>");
                    break;
                case ExpressionType.NotEqual:
                    if (b.Right is ConstantExpression && ((ConstantExpression)b.Right).Value == null) {
                        _camlBuilder.Append("<IsNotNull>");
                        Visit(b.Left);
                        _camlBuilder.Append("</IsNotNull>");
                    } else {
                        _camlBuilder.Append("<Neq>");
                        VisitBoth(b);
                        _camlBuilder.Append("</Neq>");
                    }
                    break;
                case ExpressionType.Equal:
                    if (b.Right is ConstantExpression && ((ConstantExpression)b.Right).Value == null) {
                        _camlBuilder.Append("<IsNull>");
                        Visit(b.Left);
                        _camlBuilder.Append("</IsNull>");
                    } else {
                        _camlBuilder.Append("<Eq>");
                        VisitBoth(b);
                        _camlBuilder.Append("</Eq>");
                    }
                    break;
                case ExpressionType.GreaterThan:
                    _camlBuilder.Append("<Gt>");
                    VisitBoth(b);
                    _camlBuilder.Append("</Gt>");
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    _camlBuilder.Append("<Geq>");
                    VisitBoth(b);
                    _camlBuilder.Append("</Geq>");
                    break;
                case ExpressionType.LessThan:
                    _camlBuilder.Append("<Lt>");
                    VisitBoth(b);
                    _camlBuilder.Append("</Lt>");
                    break;
                case ExpressionType.LessThanOrEqual:
                    _camlBuilder.Append("<Leq>");
                    VisitBoth(b);
                    _camlBuilder.Append("</Leq>");
                    break;

                 
            }
            return b;
        }

        private void VisitBoth(BinaryExpression b) {
            Visit(b.Left);
            Visit(b.Right);
        }
    }
}
